package com.wash.shoes.util;

import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wash.shoes.oss.ConstantProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 使用环境变量中的AK获取临时访问凭证计算签名
 */
@Slf4j
public class RAMGenerateSignature {

    public Map<String, String> getSignature() throws JsonProcessingException {

        String accessKeyId = ConstantProperties.KEYID;    //环境变量中获取access_key_id
        String accessKeySecret = ConstantProperties.KEYSECRET;    //环境变量中获取access_key_secret
        String roleArnForOssUpload = "acs:ram::1849398496984852:role/ramosstest"; // 环境变量中获取ARN
        String regionId = "cn-heyuan";    //发起STS请求所在的地域
        String roleSessionName = "ramosstest";
        Long durationSeconds = 3600L;   //临时访问凭证的有效时间

        //初始化客户端
        DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        IAcsClient client = new DefaultAcsClient(profile);

        AssumeRoleRequest request = new AssumeRoleRequest();
        request.setRoleArn(roleArnForOssUpload);
        request.setRoleSessionName(roleSessionName);
        request.setDurationSeconds(durationSeconds);

        //定义STS临时访问凭证变量
        String STSaccessKeyId = null;
        String STSsecretAccessKey = null;
        String securityToken = null;

        try {
            AssumeRoleResponse response = client.getAcsResponse(request);
            //将请求返回的STS临时访问凭证赋值到自定义变量中
            STSaccessKeyId = response.getCredentials().getAccessKeyId();
            STSsecretAccessKey = response.getCredentials().getAccessKeySecret();
            securityToken = response.getCredentials().getSecurityToken();
        } catch (ClientException e) {
            log.error("err is {0}", e);
        }
        //格式化请求日期
        long now = System.currentTimeMillis() / 1000;
        ZonedDateTime dtObj = ZonedDateTime.ofInstant(Instant.ofEpochSecond(now), ZoneId.of("UTC"));
        ZonedDateTime dtObjPlus3h = dtObj.plusHours(3);
        //请求时间
        DateTimeFormatter dtObj1Formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
        String dtObj1 = dtObj.format(dtObj1Formatter);
        //请求日期
        DateTimeFormatter dtObj2Formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String dtObj2 = dtObj.format(dtObj2Formatter);
        //请求过期时间
        DateTimeFormatter expirationTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        String expirationTime = dtObjPlus3h.format(expirationTimeFormatter);

        // 创建policy
        ObjectMapper mapper = new ObjectMapper();

        Map<String, Object> policy = new HashMap<>();
        policy.put("expiration", expirationTime);

        List<Object> conditions = new ArrayList<>();

        Map<String, String> bucketCondition = new HashMap<>();
        bucketCondition.put("bucket", ConstantProperties.BUCKETNAME);  //请替换为目标bucket名称
        conditions.add(bucketCondition);

        Map<String, String> signatureVersionCondition = new HashMap<>();
        signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
        conditions.add(signatureVersionCondition);

        Map<String, String> credentialCondition = new HashMap<>();
        credentialCondition.put("x-oss-credential", STSaccessKeyId + "/" + dtObj2 + "/" + regionId + "/oss/aliyun_v4_request"); // 替换为实际的 access key id
        conditions.add(credentialCondition);

        Map<String, String> token = new HashMap<>();
        token.put("x-oss-security-token", securityToken);
        conditions.add(token);

        Map<String, String> dateCondition = new HashMap<>();
        dateCondition.put("x-oss-date", dtObj1);
        conditions.add(dateCondition);

        policy.put("conditions", conditions);
        String jsonPolicy = mapper.writeValueAsString(policy);
        //构造待签名字符串（StringToSign）
        String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
        //计算SigningKey
        byte[] dateKey = hmacsha256(("aliyun_v4" + STSsecretAccessKey).getBytes(), dtObj2);
        byte[] dateRegionKey = hmacsha256(dateKey, regionId);
        byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
        byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
        //计算Signature
        byte[] result = hmacsha256(signingKey, stringToSign);
        String signature = BinaryUtil.toHex(result);

        Map<String, String> messageMap = new HashMap<>();
        messageMap.put("security_token", securityToken);
        messageMap.put("signature", signature);
        messageMap.put("x_oss_date", dtObj1);
        messageMap.put("x_oss_credential", STSaccessKeyId + "/" + dtObj2 + "/" + regionId + "/oss/aliyun_v4_request");
        messageMap.put("x_oss_signature_version", "OSS4-HMAC-SHA256");
        messageMap.put("policy", stringToSign);
        return messageMap;
    }

    /**
     * 使用HMAC-SHA256算法计算给定密钥和数据的哈希值的静态方法
     *
     * @param key
     * @param data
     * @return
     */
    public static byte[] hmacsha256(byte[] key, String data) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(secretKeySpec);
            return mac.doFinal(data.getBytes());
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
        }
    }
}
